﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using NHibernate;
using NHibernate.Linq;
using Mindfor.Web.Data;
using Mindfor.Web.Models;

namespace Mindfor.Web.Modules.Widgets
{
	/// <summary>
	/// Loads widgets to ViewData for current page.
	/// </summary>
	public class WidgetsLoadFilter : IMvcFilter, IResultFilter
	{
		Dictionary<string, ActionPath> m_widgetCache;

		/// <summary>
		/// When implemented in a class, gets a value that indicates whether multiple filters are allowed.
		/// </summary>
		public bool AllowMultiple
		{
			get { return false; }
		}

		/// <summary>
		/// When implemented in a class, gets the filter order.
		/// </summary>
		public int Order
		{
			get { return 0; }
		}

		public void OnResultExecuting(ResultExecutingContext filterContext)
		{
			// load widgets only for view results
			if (filterContext.Result is ViewResultBase)
			{
				ISession data = CmsApplication.DataProvider.GetCurrentSession();
				if (data != null)
				{
					Widget[] widgets;
					Page currentPage = filterContext.Controller.ViewData["Global.CurrentPage"] as Page;
					if (currentPage == null)
						widgets = data.Query<Widget>()
							.Where(r => r.Page == null)
							.OrderBy(r => r.Sort)
							.ToArray();
					else
						widgets = data.Query<Widget>()
							.Where(r => r.Page == null || r.Page == currentPage)
							.OrderBy(r => r.Sort)
							.ToArray();

					List<WidgetModel> widgetModels = new List<WidgetModel>();
					filterContext.Controller.ViewData["Global.Widgets"] = widgetModels;
					foreach (Widget w in widgets)
					{
						ActionPath action = FindWidgetAction(w);
						if (action != null)
							widgetModels.Add(new WidgetModel(w.Place, action, w.WidgetParam));
					}
				}
			}
		}

		public void OnResultExecuted(ResultExecutedContext filterContext)
		{
		}

		/// <summary>
		/// Returns widget action path.
		/// </summary>
		/// <param name="widget">Widget to search for.</param>
		private ActionPath FindWidgetAction(Widget widget)
		{
			if (m_widgetCache == null)
				LoadCache();

			// get from cache
			string cacheKey = widget.ModuleName + "=>" + widget.WidgetName;
			if (m_widgetCache.ContainsKey(cacheKey))
				return m_widgetCache[cacheKey];
			return null;
		}

		private void LoadCache()
		{
			// initialize widget cache
			m_widgetCache = new Dictionary<string, ActionPath>();
			foreach (ModuleBase module in ModuleFactory.Default.Modules.GetRegisteredModules())
			{
				foreach (ActionDescriptor action in module.GetActionDescriptors(false, false, true))
				{
					WidgetAttribute attr = action.GetCustomAttribute<WidgetAttribute>(true);
					string key = module.Name + "=>" + attr.Name;
					if (!m_widgetCache.ContainsKey(key))
					{
						ActionPath actionPath = new ActionPath(module.Name, action.ControllerDescriptor.ControllerName, action.ActionName);
						m_widgetCache.Add(key, actionPath);
					}
				}
			}
		}
	}
}
